package edu.hawaii.jmotif.sequitur.view;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.text.SimpleDateFormat;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;
import javax.swing.border.TitledBorder;
import javax.xml.datatype.XMLGregorianCalendar;
import net.miginfocom.swing.MigLayout;
import org.hackystat.utilities.stacktrace.StackTrace;
import org.hackystat.utilities.tstamp.Tstamp;
import edu.hawaii.jmotif.sequitur.controller.SequiturController;
import edu.hawaii.jmotif.sequitur.logic.MotifChartData;
import edu.hawaii.jmotif.sequitur.model.SequiturMessage;

/**
 * View component of Sequitur MVC GUI.
 * 
 * @author psenin
 * 
 */
public class SequiturView implements Observer, ActionListener {

  // relevant string constants go here
  //
  private static final String COMMA = ",";
  private static final String CR = "\n";

  // String is the king - constants for actions
  //
  /** Load data action key. */
  private static final String LOAD_DATA = "load_data";
  /** Process data action key. */
  private static final String PROCESS_DATA = "process_data";

  /** Frame for the GUI. */
  private static final JFrame frame = new JFrame("JMotif and Sequitur. v. 0.784");

  /** The main menu bar. */
  private static final JMenuBar menuBar = new JMenuBar();

  /** Global controller handler - controller is supplier of action handlers. */
  private SequiturController controller;

  // data source related variables
  //
  private JPanel dataSourcePane;
  private JTextField dataFileTxtField;
  private JButton selectFileButton;
  private JTextField dataRowsLimitTxtField;

  // SAX parameters related fields
  //
  private JPanel saxParametersPane;
  private JTextField SAXwindowSizeField;
  private JTextField SAXpaaSizeField;
  private JTextField SAXalphabetSizeField;

  // data chart place
  //
  private SequiturChartPanel dataChartPane;

  // sequitur rule list
  //
  private SequiturRulesPanel sequiturRulesPane;

  private SequiturRuleChartPanel ruleChartPane;

  // workflow pane - buttons
  //
  private JPanel workflowManagementPane;
  private JButton dataLoadButton;
  private JButton processButton;
  private JButton displayButton;

  // The log panel configuration section
  //
  private SimpleDateFormat logDateFormat = new SimpleDateFormat("HH:mm:ss' '");
  private static final JTextArea logTextArea = new JTextArea();
  private static final JScrollPane logTextPane = new JScrollPane(logTextArea,
      ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

  /**
   * Constructor.
   * 
   * @param controller The controller used for the application flow control.
   */
  public SequiturView(SequiturController controller) {
    this.controller = controller;
  }

  /**
   * Shows the GUI.
   */
  public void showGUI() {
    // Schedule a job for the event-dispatching thread:
    // creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        try {
          // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
          UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        }
        catch (Exception e) {
          System.err.print(StackTrace.toString(e));
        }
        configureGUI();
      }
    });
  }

  /**
   * Initialize the dialog
   */
  private void configureGUI() {

    // set look and fill
    JFrame.setDefaultLookAndFeelDecorated(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // build main components
    buildMenuBar();

    buildDataSourcePane();
    buildSAXParamsPane();

    buildChartPane();

    buildSequiturPane();

    buildWorkflowPane();

    buildLogPane();

    // put listeners in place
    sequiturRulesPane.addPropertyChangeListener(dataChartPane);
    sequiturRulesPane.addPropertyChangeListener(ruleChartPane);
    
    dataChartPane.addPropertyChangeListener(SequiturMessage.MAIN_CHART_CLICKED_MESSAGE, sequiturRulesPane);

    // set the main panel layout
    MigLayout mainFrameLayout = new MigLayout("", "[fill,grow,center]",
        "[][][fill,grow 50][fill,grow 50][][]");
    frame.getContentPane().setLayout(mainFrameLayout);

    // set the menu bar
    frame.setJMenuBar(menuBar);

    // place panels
    frame.getContentPane().add(dataSourcePane, "wrap");
    frame.getContentPane().add(saxParametersPane, "wrap");

    frame.getContentPane().add(dataChartPane, "wrap");

    frame.getContentPane().add(sequiturRulesPane, "w 70%, split");

    frame.getContentPane().add(ruleChartPane, "w 30%, wrap");

    frame.getContentPane().add(workflowManagementPane, "wrap");

    frame.getContentPane().add(logTextPane, "h 60:80:80,wrap");

    // Show frame
    frame.pack();

    frame.setSize(new Dimension(900, 840));
    frame.setVisible(true);
  }

  /**
   * Build the application menu bar.
   */
  private void buildMenuBar() {

    // Build the File menu.
    JMenu fileMenu = new JMenu("File");
    fileMenu.setMnemonic(KeyEvent.VK_F);
    fileMenu.getAccessibleContext().setAccessibleDescription("The file menu");

    // an exit item
    JMenuItem exitItem = new JMenuItem("Exit", KeyEvent.VK_X);
    exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK));
    exitItem.getAccessibleContext().setAccessibleDescription("Exit from here");
    exitItem.addActionListener(this);
    fileMenu.add(exitItem);

    // Build the About menu.
    JMenu helpMenu = new JMenu("Help");
    helpMenu.setMnemonic(KeyEvent.VK_F1);
    // helpMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1,
    // ActionEvent.ACTION_PERFORMED));
    helpMenu.getAccessibleContext().setAccessibleDescription("Help & About");

    // a help item
    JMenuItem helpItem = new JMenuItem("Help", KeyEvent.VK_H);
    helpItem.getAccessibleContext().setAccessibleDescription("Get some help here.");
    exitItem.addActionListener(controller);
    helpMenu.add(helpItem);

    // an about item
    JMenuItem aboutItem = new JMenuItem("About", KeyEvent.VK_A);
    aboutItem.getAccessibleContext().setAccessibleDescription("About the app.");
    exitItem.addActionListener(controller);
    helpMenu.add(aboutItem);

    menuBar.add(fileMenu);
    menuBar.add(helpMenu);
  }

  private void buildDataSourcePane() {

    dataSourcePane = new JPanel();
    // insets: T, L, B, R.
    dataSourcePane.setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createEtchedBorder(BevelBorder.LOWERED), "Data source", TitledBorder.LEFT,
        TitledBorder.CENTER, new Font("times new roman", Font.PLAIN, 10)));
    MigLayout dataSourcePaneLayout = new MigLayout("insets 3 3 3 3",
        "[][fill,grow][]10[][fill,grow]", "[]");

    dataSourcePane.setLayout(dataSourcePaneLayout);

    // file section
    //
    // label
    JLabel fileNameLabel = new JLabel("Data file: ");
    // field
    dataFileTxtField = new JTextField("");
    fileNameLabel.setLabelFor(dataFileTxtField);
    // the load file button
    selectFileButton = new JButton("Browse...");
    selectFileButton.setMnemonic('B');
    
    dataSourcePane.add(fileNameLabel, "");
    dataSourcePane.add(dataFileTxtField, "");
    dataSourcePane.add(selectFileButton, "");

    // data rows interval section
    //
    JLabel lblCountRows = new JLabel("Row limit (0=all):");
    dataRowsLimitTxtField = new JTextField("0");
    dataSourcePane.add(lblCountRows, "");
    dataSourcePane.add(dataRowsLimitTxtField, "");

    // add the action listener
    //
    selectFileButton.addActionListener(controller.getSelectFileListener());
    dataFileTxtField.getDocument().addDocumentListener(controller.getFilenameListener());
  }

  private void buildSAXParamsPane() {

    saxParametersPane = new JPanel();
    saxParametersPane.setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createEtchedBorder(BevelBorder.LOWERED), "SAX params", TitledBorder.LEFT,
        TitledBorder.CENTER, new Font("times new roman", Font.PLAIN, 10)));

    // insets: T, L, B, R.
    MigLayout saxPaneLayout = new MigLayout("insets 3 2 2 2",
        "[][fill,grow]10[][fill,grow]10[][fill,grow]", "[]");
    saxParametersPane.setLayout(saxPaneLayout);

    JLabel windowSizeLabel = new JLabel("Window size:");
    SAXwindowSizeField = new JTextField("100");

    JLabel paaSizeLabel = new JLabel("PAA size:");
    SAXpaaSizeField = new JTextField("4");

    JLabel alphabetSizeLabel = new JLabel("Alphabet size:");
    SAXalphabetSizeField = new JTextField("3");

    saxParametersPane.add(windowSizeLabel);
    saxParametersPane.add(SAXwindowSizeField);
    saxParametersPane.add(paaSizeLabel);
    saxParametersPane.add(SAXpaaSizeField);
    saxParametersPane.add(alphabetSizeLabel);
    saxParametersPane.add(SAXalphabetSizeField);

  }

  private void buildChartPane() {
    // MotifChartPanel _chart = new MotifChartPanel(null);
    dataChartPane = new SequiturChartPanel();
    dataChartPane.setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createEtchedBorder(BevelBorder.LOWERED), "Data display", TitledBorder.LEFT,
        TitledBorder.CENTER, new Font("times new roman", Font.PLAIN, 10)));
    MigLayout chartPaneLayout = new MigLayout("insets 0 0 0 0", "[fill,grow]", "[fill,grow]");
    dataChartPane.setLayout(chartPaneLayout);
  }

  private void buildSequiturPane() {
    sequiturRulesPane = new SequiturRulesPanel();
    sequiturRulesPane.setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createEtchedBorder(BevelBorder.LOWERED), "Sequitur grammar (search in list by clicking into list and pressing CTRL-F)",
        TitledBorder.LEFT, TitledBorder.CENTER, new Font("times new roman", Font.PLAIN, 10)));
    MigLayout sequiturPaneLayout = new MigLayout(",insets 0 0 0 2", "[fill,grow]", "[fill,grow]");
    sequiturRulesPane.setLayout(sequiturPaneLayout);

    ruleChartPane = new SequiturRuleChartPanel();
    ruleChartPane.setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createEtchedBorder(BevelBorder.LOWERED), "Rule chart", TitledBorder.LEFT,
        TitledBorder.CENTER, new Font("times new roman", Font.PLAIN, 10)));
    MigLayout ruleChartPaneLayout = new MigLayout(",insets 0 2 0 0", "[fill,grow]", "[fill,grow]");
    ruleChartPane.setLayout(ruleChartPaneLayout);
  }

  private void buildWorkflowPane() {

    workflowManagementPane = new JPanel();
    workflowManagementPane.setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createEtchedBorder(BevelBorder.LOWERED),
        "Workflow management: load > process > display", TitledBorder.LEFT, TitledBorder.CENTER,
        new Font("times new roman", Font.PLAIN, 10)));
    MigLayout workflowPaneLayout = new MigLayout(",insets 2 2 2 2", "[fill,grow]", "[fill,grow]");
    workflowManagementPane.setLayout(workflowPaneLayout);

    dataLoadButton = new JButton("Load data");
    dataLoadButton.setMnemonic('L');
    // add the action listener
    dataLoadButton.setActionCommand(LOAD_DATA);
    dataLoadButton.addActionListener(this);

    processButton = new JButton("Process data");
    processButton.setMnemonic('P');
    processButton.setActionCommand(PROCESS_DATA);
    processButton.addActionListener(this);

    displayButton = new JButton("Display graphs");
    displayButton.setMnemonic('D');
    displayButton.addActionListener(controller.getDisplayCommandListener());

    workflowManagementPane.add(dataLoadButton);
    workflowManagementPane.add(processButton);
    workflowManagementPane.add(displayButton);

  }

  /**
   * Build the logging panel.
   */
  private void buildLogPane() {
    // logging panel
    logTextArea.setFont(new Font("MonoSpaced", Font.PLAIN, 10));
    logTextArea.setEditable(false);
    logTextArea.setCaretPosition(logTextArea.getDocument().getLength());
    logTextPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    logTextPane.setAutoscrolls(true);
    log(Level.INFO, "running Sequitur Demo... ");
  }

  /**
   * Shut downs the application.
   */
  private void shutdown() {
    Runtime.getRuntime().exit(0);
  }

  /**
   * Logs message.
   * 
   * @param level The logging level to use.
   * @param message The log message.
   */
  protected void log(Level level, String message) {
    message.replaceAll("\n", "");
    XMLGregorianCalendar now = Tstamp.makeTimestamp();
    String dateStr = logDateFormat.format(now.toGregorianCalendar().getTime());
    if (message.startsWith("model") || message.startsWith("controller")) {
      logTextArea.append(dateStr + message + CR);
    }
    else {
      logTextArea.append(dateStr + "view: " + message + CR);
    }
    logTextArea.setCaretPosition(logTextArea.getDocument().getLength());
  }

  @Override
  public void update(Observable o, Object arg) {
    if (arg instanceof SequiturMessage) {

      SequiturMessage message = (SequiturMessage) arg;

      // new FileName
      //
      if (SequiturMessage.DATA_FNAME.equalsIgnoreCase(message.getType())) {
        this.dataFileTxtField.setText((String) message.getPayload());
        this.dataFileTxtField.repaint();
      }

      // new log message
      //
      else if (SequiturMessage.STATUS_MESSAGE.equalsIgnoreCase(message.getType())) {
        log(Level.ALL, (String) message.getPayload());
      }

      // chart object
      //
      else if (SequiturMessage.CHART_MESSAGE.equalsIgnoreCase(message.getType())) {

        MotifChartData chartData = (MotifChartData) message.getPayload();

        // setting the chart first
        //
        dataChartPane.set(chartData);

        // and the rules pane second
        //
        sequiturRulesPane.set(chartData);

        // and the "snapshots panel"
        //
        ruleChartPane.set(chartData);

        // dataChartPane.getChart().setNotify(true);
        frame.validate();
        frame.repaint();
      }
    }

  }

  @Override
  public void actionPerformed(ActionEvent arg) {
    String command = arg.getActionCommand();
    if (LOAD_DATA.equalsIgnoreCase(command)) {
      String loadLimit = this.dataRowsLimitTxtField.getText();
      this.controller.getLoadFileListener().actionPerformed(new ActionEvent(this, 1, loadLimit));
    }

    else if (PROCESS_DATA.equalsIgnoreCase(command)) {
      String window = this.SAXwindowSizeField.getText();
      String paa = this.SAXpaaSizeField.getText();
      String alpphabet = this.SAXalphabetSizeField.getText();
      this.controller.getProcessDataListener().actionPerformed(
          new ActionEvent(this, 2, window + COMMA + paa + COMMA + alpphabet));
    }

    else if ("Exit".equalsIgnoreCase(command)) {
      shutdown();
    }
  }
}
